

import importlib

def import_from_string(module_name, func_name):
    # 动态导入模块
    module = importlib.import_module(module_name)
    # 获取函数
    func = getattr(module, func_name)
    return func


class IOHandler:

    def __init__(self,config):
        self.config = config
        self.name = str(config["name"])
        self.type = str(config["type"])
        self.class_name = str(self.type.split(".")[-1])
        self.base_module_path = self.type.replace("."+self.class_name,"")
        self.__load_handler()
    
    def __load_handler(self):
        self.handler_class = None
        try:
            self.handler_class = import_from_string(
                self.base_module_path,
                self.class_name
            )
        except Exception as e:
            print(f"error when load handler, config: {self.config}")
            self.handler_class = None
        
    def get_name(self):
        return self.name
    
    def get_handler_class_name(self):
        return self.class_name
    
    def get_handler_class(self):
        return self.handler_class
    
    def __call__(self, *args, **kwds):
        if self.handler_class is None:
            raise Exception("handler class is None, cannot call it")
        return self.handler_class(*args,**kwds)
    

class InputHandler(IOHandler):
    def __init__(self, config):
        super().__init__(config)

class OutputHandler(IOHandler):
    def __init__(self, config):
        super().__init__(config)


class OperNode:

    SOURCE = "source"
    FILTER = "filter"
    TAG = "tag"
    MERGE = "merge"
    SINK = "sink"
    CUSTOM = "custom"
    UNKNOWN = "unknown"

    OperSet = [ SOURCE,FILTER,TAG,MERGE,SINK,CUSTOM,UNKNOWN ]

    def __init__(self,node_config):
        self.ori = node_config
        self.node_id = node_config["node_id"]
        self.optype = self.__check_opertype(node_config["optype"])
        self.cell_config = node_config["config"]
        # outputs handle
        self.outputs = {}
        self.inputs = {}
        if "outputs" in node_config:
            self.__handle_outputs(node_config["outputs"])
        if "inputs" in node_config:
            self.__handle_inputs(node_config["inputs"])
    
    def getOperType(self):
        return self.optype
    
    def getInputClass(self,name:str):
        if name not in self.inputs:
            raise Exception(f"input class: {name} cannot found in node_id={self.node_id}")
        return self.inputs[name]
     
    def getOutputClass(self,name:str):
        if name not in self.outputs:
            raise Exception(f"outputs class: {name} cannot found in node_id={self.node_id}")
        return self.outputs[name]
    
    def __handle_outputs(self,outputs:list):
        for handle_config in outputs:
            handler = OutputHandler(handle_config)
            self.outputs[handler.get_name()] = handler
     
    def __handle_inputs(self,outputs:list):
        for handle_config in outputs:
            handler = InputHandler(handle_config)
            self.inputs[handler.get_name()] = handler
    
    def __check_opertype(self,opertype:str):
        op = opertype.upper()
        for oper in self.OperSet:
            if op == oper.upper():
                return oper
        return self.UNKNOWN




class MegaConfig:

    def __init__(self,config:dict):
        self.config = config
        self.elements = config["elements"]
        self.dag = config["dag"]
        self.global_config = config["global_config"]
    
    def __parse_node(self,node):
        """
        {
            "node_id": "3_0",
            "optype": "SOURCE",
            "config": {
                "topic": "log"
            },
            "outputs": [
                {
                    "name": "overall_data",
                    "type": "EntrySet"
                }
            ]
        },
        """

        pass


